import 'package:flutter/material.dart';
import 'dart:async';
import './bloc_base.dart';
import '../model/main_role.dart';
import '../model/other_role.dart';

import '../model/my_socket.dart';
import '../model/socket_msg.dart';
import '../user_proof.dart';
import '../model/player.dart';
import './main_role_bloc.dart';
import './other_role_bloc.dart';
import '../model/hand_cards.dart';

class RoomBloc implements BlocBase {
  final BuildContext roomContext;
  String currentPlayerId;
  MainRoleBloc mainRoleBloc;
  Map<String, OtherRoleBloc> otherRoleBlocs;
  List<String> otherRoleLastCards;

  Map<String, dynamic> roomStatusData;

  MySocket socket;
  static SocketListenerPage listenerPageName = SocketListenerPage.room;

  StreamController<Map<String, OtherRole>> _otherRolesController = StreamController<Map<String, OtherRole>>();
  Stream<Map<String, OtherRole>> get otherRolesStream => _otherRolesController.stream;
  StreamSink<Map<String, OtherRole>> get otherRolesSink => _otherRolesController.sink;

  Map<String, OtherRole> get otherRoleData => otherRoleBlocs.map((key, value) => MapEntry(key, value.roleData));
  MainRole get mainRoleData => mainRoleBloc.roleData;
  
  RoomBloc({@required this.roomContext}) {
    mainRoleBloc = MainRoleBloc(roleData: MainRole(roleId: UserProof.userId, uname: UserProof.userInfo!=null?UserProof.userInfo['uname']:'', sex: UserProof.userInfo!=null?UserProof.userInfo['sex']:1, asset: UserProof.userInfo!=null?UserProof.userInfo['asset']:0));
    otherRoleBlocs = {};
    MySocket.addListener(listenerPge: listenerPageName, callback: this.blocListener);
  }

  OtherRole getOtherRoleData(String roleId) {
    return otherRoleBlocs[roleId].roleData;
  }
  OtherRoleBloc getOtherRoleBloc(String roleId) {
    return otherRoleBlocs[roleId];
  }
  
  void blocListener(dynamic data) {
    var tData = data['data'];
    ResMsgType msgType = SocketMsg.resMsgTypeFromStr[data['type']];
      switch (msgType) {
        case ResMsgType.enterRoom:
          this.enterRoom(tData);
          break;
        case ResMsgType.otherEnterRoom:
          this.otherEnterRoom(tData);
          break;
        case ResMsgType.otherOutRoom:
          this.otherOutRoom(tData);
          break;
        case ResMsgType.dealCard:
          this.dealCard(tData);
          break;
        case ResMsgType.statusChange:
          this.roleStatusChange(tData as Map);
          break;
        case ResMsgType.gameEnd:
          this.gameEnd(tData);
          break;
        case ResMsgType.setOtherRoleStatus:
          this.setOtherRoleStatus(List.from(tData));
          break;
        default: break;
      }
  }

  void setOtherRoleStatus(List otherroleInfo) {
    otherroleInfo.forEach((otherRole){
      getOtherRoleBloc(otherRole['id'].toString()).triggerStatus(PlayerStatus.values[otherRole['status']]==PlayerStatus.ready?PlayerStatus.ready:PlayerStatus.notReady);
    });
    mainRoleBloc.setLandlord(false);
    opOtherRoleBloc((otherRoleBloc){
      otherRoleBloc.setLandlord(false);
    });
  }
  void gameEnd(Map gameEndRes) {
    gameEndRes['players'].forEach((player){
      if(player['id'] != UserProof.userId) {
        getOtherRoleBloc(player['id']).triggerStatus(PlayerStatus.isRestart);
        getOtherRoleBloc(player['id']).checkAsset(player['asset']);
      }else {
        mainRoleBloc.triggerStatus(PlayerStatus.isRestart);
        mainRoleBloc.checkAsset(player['asset']);
      }
    });
  }
  void enterRoom(List players) {
    if(players.length > 0) {
      String playerId;
      PlayerStatus playerStatus;
      OtherRoleBloc currentOtherRoleBloc;
      players.forEach((player){
        playerId = player['id'];
        playerStatus = PlayerStatus.values[player['status']]==PlayerStatus.ready?PlayerStatus.ready:PlayerStatus.notReady;
        currentOtherRoleBloc = OtherRoleBloc(roleData: OtherRole(roleId: playerId, disToMainRole: player['disToMainRole'], uname: player['uname'], sex: player['sex'], asset: player['asset']));
        if(playerStatus != null) {
          currentOtherRoleBloc.triggerStatus(playerStatus);
        }
        otherRoleBlocs[playerId] = currentOtherRoleBloc;
      });
      otherRolesSink.add(otherRoleData);
    }
  }

  void otherEnterRoom(Map player) {
    String playerId = player['id'];
    otherRoleBlocs[playerId] = OtherRoleBloc(roleData: OtherRole(roleId: playerId, disToMainRole: player['disToMainRole'], uname: player['uname'], sex: player['sex'], asset: player['asset']));
    otherRolesSink.add(otherRoleData);
  }

  void roleStatusChange(Map tData) {
   String playerId = tData['id'];
   PlayerStatus playerStatus = PlayerStatus.values[tData['status']];
   if(playerId == UserProof.userId) {
      if(playerStatus == PlayerStatus.waiting) {
        this.otherRoleLastCards = null;
        mainRoleBloc.playCardsSuc(sucCards: null == tData['cards'] ? null : List<String>.from(tData['cards']));
      }else{
        if(playerStatus == PlayerStatus.thinking) {
          List<String> cardsData = null == tData['cards'] ? null : List<String>.from(tData['cards']);
          if(null != cardsData) {
            this.otherRoleLastCards = cardsData;
          }
          mainRoleBloc.setOtherRoleLastCards(this.otherRoleLastCards);
        }
        mainRoleBloc.triggerStatus(playerStatus);
        if(null != tData['asset']) {
          mainRoleBloc.checkAsset(tData['asset']);
        }
      }
    }else {
      if(playerStatus == PlayerStatus.waiting) {
        List<String> cardsData = null == tData['cards'] ? null : List<String>.from(tData['cards']);
        if(null != cardsData) {
          this.otherRoleLastCards = cardsData;
        }
        getOtherRoleBloc(playerId).playCardsSuc(sucCards: cardsData);
      }else {
        getOtherRoleBloc(playerId).triggerStatus(playerStatus);
      }
      if(null != tData['asset']) {
        getOtherRoleBloc(playerId).checkAsset(tData['asset']);
      }
    }
  }

  void otherOutRoom(Map player) {
    String playerId = player['id'];
    getOtherRoleBloc(playerId).dispose();
    otherRoleBlocs.remove(playerId);
    otherRolesSink.add(otherRoleData);
  }
  void sendMsg({SendMsgType type, Map data}) {
    MySocket.sendMsg(type: type, data: data);
  }
  void mainRoleGetCards(List cards) {
    mainRoleBloc.getCards(cards);
  }
  void otherRoleGetCards(String otherRoleId, List cards) {
    getOtherRoleBloc(otherRoleId).getCards(cards);
  }
  void dealCard(Map cardData) {
    List cards = cardData['card'];
    int cardCout = cards.length, cardIndex=0;

    mainRoleBloc.setMaxCountCards(cardCout);
    mainRoleBloc.triggerStatus(PlayerStatus.getCard);
    this.opOtherRoleBloc((otherRoleBloc){
      otherRoleBloc.setMaxCountCards(cardCout);
      otherRoleBloc.triggerStatus(PlayerStatus.getCard);
    });
    Timer.periodic(Duration(milliseconds: 100), (_timer){
      if(cardIndex == cardCout) {
        MySocket.sendMsg(type: SendMsgType.readyCards);
        mainRoleBloc.sortCards();
        _timer.cancel();
      }else {
        if(cardIndex == 0) {
          mainRoleBloc.setCards([cards[cardIndex++]]);
          this.opOtherRoleBloc((otherRoleBloc){
            otherRoleBloc.setCards(['']);
          });
        }else {
          mainRoleBloc.getCards([cards[cardIndex++]]);
          this.opOtherRoleBloc((otherRoleBloc){
            otherRoleBloc.getCards(['']);
          });
        }
      }
    });
  }

  void opOtherRoleBloc(Function(OtherRoleBloc) callbak) {
    otherRoleBlocs.forEach((playerId, otherRoleBloc) {
      callbak(otherRoleBloc);
    });
  }
  void passCards() {
    MySocket.sendMsg(type: SendMsgType.playCards);
  }
  void playCards({List<String> cards, CardsType cardsType}) {
    MySocket.sendMsg(type: SendMsgType.playCards, data: {'cards': cards, 'type': cardsType.index});
  }
  void sendReadyMsg() {
    this.sendMsg(type: SendMsgType.ready);
  }
  void goonGame() {
    this.sendMsg(type: SendMsgType.restart);
  }
  void sendOutRoom() {
    this.sendMsg(type: SendMsgType.outRoom);
  }
  //实现BlocBase类的方法
  void dispose(){
    MySocket.removeListener(listenerPge: listenerPageName);

    otherRolesSink.close();
    _otherRolesController.close();

    mainRoleBloc.dispose();

    otherRoleBlocs.forEach((playerId, otherRoleBloc){
      otherRoleBloc.dispose();
    });
  }
}